Drawing ActiveX Control Into Memory Buffer

zZz/KCN

Why?

One day I wanted to add support for Flash into our demo engine. It had to be drawn as an overlay over the main demo window. There were several ways to accomplish this. One was to render Flash myself, as the SWF format is open. The other was to use the standard Macromedia Flash Player ActiveX component. So I've chosen the second way.

I will explain how I made the Flash Player work for me. The main thing about it is that any ActiveX component can be drawn into a memory buffer like this, not only Flash Player.

I am not a COM/OLE/ActiveX expert, so there could possibly be better ways to do this and that.

What do we need?

First, we need Visual C++ 6.0 with ATL libraries installed. OLE View tool may also be needed. And also optionally MIDL-compiler, but I believe it's installed by default.

Second, we need a component interface description. It would be best if you could get it from the developer. If not, as in case of Flash Player, then have a look at the next paragraph.

The components are different, how do I know how to use them?

What you need is a component interface description. This is a C++ interface class declaration for your component. To get it, run OLE View tool, push View Type Lib button and browse to your component. Push Save button and save IDL file. Then use MIDL compiler to get .c and .h files from IDL: midl swflash.idl /h flash.h. You could also save .h and .c from OLE View, but I have actually not tried it ;)

OK, you got header and definitions for your component. The definitions .c file contains ClassID (CLSID_) and RefID(IID_) for your component. Include these files into your project.

Now, to call your component's functions, you call the functions of this interface. Component properties should be available via put_xxxx and get_xxxx functions.

Have a look at the OLE View descriptions of functions - it will show which functions parameters are used as inputs and which as outputs.

So, now you know which functions and properties your component has, and what parameters they expect. If you still don’t understand how your component works, then... hmm... then you're stuck ;)

The main principle

Drawing an ActiveX component is not an easy one-touch task even if you are already not new to ActiveX. The reason is that you should implement classes for ActiveX container. You can of course implement them yourself, but it's far too much useless work in my opinion. Programs like Delphi, Visual Basic and others provide implementations themselves. Using Visual C++ you have an implementation in MFC, though MFC in a demo or a game is not the best solution ;). You also have an implementation in ATL library. This is the solution I've chosen, because it is not so heavy-weighted as MFC and can be used in programs that don't use MFC but still need ActiveX support. ATL can be used as a DLL or as a static library. The footprint of a static library is small enough to believe it can be used even in some kind of intro.

So the first thing to do is to setup ATL. Then you create an instance of your interface in ATL window. You will get a pointer to IUnknown interface. Using QueryInterface of this IUnknown you get your interface pointer:

HRESULT hr=unk->QueryInterface(IID_IShockwaveFlash,(void **)&iflash);

But this is not the only interface you need. To draw a component you need to query for another interface - IViewObject (or IViewObject2, or IViewObjectEX):

iflash->QueryInterface(IID_IViewObjectEx,(void **)&viewobject);

Now you setup the frame buffer. It is actually a DIB section. You create a compatible DC for your screen, then, create a DIB section and select it onto this DC. A compatible DC is a DC that is stored in system memory. A DIB section is a bitmap in which user can access the image buffer directly in memory.

At last, we actually render the component onto our DIB section (loop it):

viewobject->Draw(DVASPECT_CONTENT, -1, NULL, NULL, NULL, hdcCompatible,
	&rectl, &rectl, NULL, NULL);

What we get is an image of component drawn into our frame buffer. Note that it will be in BGR or BGRX format if you specify 24 or 32 bit color depth.

Creating a frame buffer

Everything is obvious:

HDC desktop_dc=GetDC(GetDesktopWindow());
hdcCompatible = CreateCompatibleDC(desktop_dc);	

BITMAPINFO binfo;
memset(&binfo,0,sizeof(binfo));
binfo.bmiHeader.biSize=sizeof(binfo);
binfo.bmiHeader.biPlanes=1;
binfo.bmiHeader.biBitCount=32;
binfo.bmiHeader.biCompression=BI_RGB;
binfo.bmiHeader.biHeight=height;
binfo.bmiHeader.biWidth=width;

hBitmap=CreateDIBSection(hdcCompatible,&binfo,DIB_RGB_COLORS,&frame_buffer,
	NULL,0);
SelectObject(hdcCompatible, hBitmap);

Initializing ATL

One thing not obvious here is that you need to declare a module variable:

#include <atlbase.h>
CComModule _Module;
#include <atlwin.h>

Then declare a window class:

CAxWindow cw;
cw.Create(NULL,&rc,flash_clsid_c,0);

You will also need here a CLSID of your component in a text form:

char *flash_clsid_c="{D27CDB6E-AE6D-11CF-96B8-444553540000}";

Well, you will need to manually create it according to MIDL-generated structures:

const CLSID CLSID_ShockwaveFlash = {0xD27CDB6E, 0xAE6D, 0x11CF,
{0x96, 0xB8, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}};

Then, during creation of frame buffer you will have to resize the ATL window to the extents of the buffer. This is required.

RECT rc={0,0,width,height};
AdjustWindowRect(&rc,cw.GetWindowLong(GWL_STYLE),FALSE);
cw.SetWindowPos(NULL,0,0,rc.right-rc.left,
	rc.bottom-rc.top,
        SWP_NOMOVE|SWP_NOREDRAW|SWP_DEFERERASE|SWP_NOACTIVATE|
	SWP_NOCOPYBITS|SWP_NOOWNERZORDER|SWP_NOSENDCHANGING|SWP_NOZORDER);

Registering a component

If you wish to distribute a component with your program, you could ship .ocx of your component. Then you check, if your component is already installed in the system by using, e.g. CoCreateInstance call, and if it's not installed - register you .ocx.

To register it, you load it as DLL using LoadLibrary, get entry point to DllRegisterServer function and simply call it. It doesn’t require any parameters. In most cases any ActiveX component can be registered like this.

Sources

The source code quotes in this article are taken from my ZFLASH library, which is available from our site and maybe it is in the HUGI bonus pack.

--

zZz/KCN aka Anatoliy Samara

6mar2003

Makeevka

Ukraine